//
//  UINavigationController+SFNavigationBarManager.m
//  Pods-SFNavigationBarManager_Example
//
//  Created by shen on 2021/11/17.
//

#import "UINavigationController+SFNavigationBarManager.h"
#import <objc/runtime.h>
#import "SFSwizzle.h"
#import "UIViewController+SFNavigationBarManager.h"

@implementation NSObject (SFNavigationBarManager)

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        SFSwizzleMethod(objc_getClass("_UIBarBackground"),
                        @selector(setHidden:),
                        [self class],
                        @selector(sf_setHidden:));
    });
}

- (void)sf_setHidden:(BOOL)hidden {
    UIResponder *responder = (UIResponder *)self;
    while (responder) {
        if ([responder isKindOfClass:[UINavigationBar class]] && ((UINavigationBar *)responder).sf_isFakeBar) {
            return;
        }
        if ([responder isKindOfClass:[UINavigationController class]]) {
            [self sf_setHidden:((UINavigationController *)responder).sf_backgroundViewHidden];
            return;
        }
        responder = responder.nextResponder;
    }
    [self sf_setHidden:hidden];
}

@end

@implementation UINavigationBar (SFNavigationBarManager)

#ifdef __IPHONE_11_0
+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        SFSwizzleMethod([self class],
                        @selector(layoutSubviews),
                        [self class],
                        @selector(sf_layoutSubviews));
    });
}
#endif

- (void)sf_layoutSubviews {
    [self sf_layoutSubviews];
    UIView *backgroundView = [self valueForKey:@"_backgroundView"];
    CGRect frame = backgroundView.frame;
    frame.size.height = self.frame.size.height + fabs(frame.origin.y);
    backgroundView.frame = frame;
}

- (BOOL)sf_isFakeBar {
    return [objc_getAssociatedObject(self, _cmd) boolValue];
}

- (void)setSf_isFakeBar:(BOOL)sf_isFakeBar {
    objc_setAssociatedObject(self, @selector(sf_isFakeBar), @(sf_isFakeBar), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

@end

@implementation UINavigationController (SFNavigationBarManager)

#pragma mark - Runtime
+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        SFSwizzleMethod([self class], @selector(pushViewController:animated:),
                        [self class], @selector(sf_pushViewController:animated:));

        SFSwizzleMethod([self class], @selector(popViewControllerAnimated:),
                        [self class], @selector(sf_popViewControllerAnimated:));

        SFSwizzleMethod([self class], @selector(popToViewController:animated:),
                        [self class], @selector(sf_popToViewController:animated:));

        SFSwizzleMethod([self class], @selector(popToRootViewControllerAnimated:),
                        [self class], @selector(sf_popToRootViewControllerAnimated:));

        SFSwizzleMethod([self class], @selector(setViewControllers:animated:),
                        [self class], @selector(sf_setViewControllers:animated:));
    });
}

- (void)sf_pushViewController:(UIViewController *)viewController animated:(BOOL)animated {
    UIViewController *disappearingViewController = self.viewControllers.lastObject;
    if (!disappearingViewController) {
        return [self sf_pushViewController:viewController animated:animated];
    }
    if (!self.sf_transitionContextToViewController || !disappearingViewController.sf_transitionNavigationBar) {
        [disappearingViewController sf_addTransitionNavigationBarIfNeeded];
    }
    if (animated) {
        self.sf_transitionContextToViewController = viewController;
        if (disappearingViewController.sf_transitionNavigationBar) {
            disappearingViewController.navigationController.sf_backgroundViewHidden = YES;
        }
    }
    return [self sf_pushViewController:viewController animated:animated];
}

- (UIViewController *)sf_popViewControllerAnimated:(BOOL)animated {
    if (self.viewControllers.count < 2) {
        return [self sf_popViewControllerAnimated:animated];
    }

    UIViewController *disappearingViewController = self.viewControllers.lastObject;
    [disappearingViewController sf_addTransitionNavigationBarIfNeeded];
    UIViewController *appearingViewController = self.viewControllers[self.viewControllers.count - 2];
    if (appearingViewController.sf_transitionNavigationBar) {
        UINavigationBar *appearingNavigationBar = appearingViewController.sf_transitionNavigationBar;
        if (@available(iOS 15, *)) {
            self.navigationBar.standardAppearance = appearingNavigationBar.standardAppearance;
            self.navigationBar.scrollEdgeAppearance = appearingNavigationBar.scrollEdgeAppearance;
        } else {
            self.navigationBar.barTintColor = appearingNavigationBar.barTintColor;
            [self.navigationBar setBackgroundImage:[appearingNavigationBar backgroundImageForBarMetrics:UIBarMetricsDefault] forBarMetrics:UIBarMetricsDefault];
            self.navigationBar.shadowImage = appearingNavigationBar.shadowImage;
        }
    }
    if (animated) {
        disappearingViewController.navigationController.sf_backgroundViewHidden = YES;
    }
    return [self sf_popViewControllerAnimated:animated];
}

- (NSArray<UIViewController *> *)sf_popToViewController:(UIViewController *)viewController animated:(BOOL)animated {
    if (![self.viewControllers containsObject:viewController] || self.viewControllers.count < 2) {
        return [self sf_popToViewController:viewController animated:animated];
    }

    UIViewController *disappearingViewController = self.viewControllers.lastObject;
    [disappearingViewController sf_addTransitionNavigationBarIfNeeded];
    if (viewController.sf_transitionNavigationBar) {
        UINavigationBar *appearingNavigationBar = viewController.sf_transitionNavigationBar;
        if (@available(iOS 15, *)) {
            self.navigationBar.standardAppearance = appearingNavigationBar.standardAppearance;
            self.navigationBar.scrollEdgeAppearance = appearingNavigationBar.scrollEdgeAppearance;
        } else {
            self.navigationBar.barTintColor = appearingNavigationBar.barTintColor;
            [self.navigationBar setBackgroundImage:[appearingNavigationBar backgroundImageForBarMetrics:UIBarMetricsDefault] forBarMetrics:UIBarMetricsDefault];
            self.navigationBar.shadowImage = appearingNavigationBar.shadowImage;
        }
    }
    if (animated) {
        disappearingViewController.navigationController.sf_backgroundViewHidden = YES;
    }
    return [self sf_popToViewController:viewController animated:animated];
}

- (NSArray<UIViewController *> *)sf_popToRootViewControllerAnimated:(BOOL)animated {
    if (self.viewControllers.count < 2) {
        return [self sf_popToRootViewControllerAnimated:animated];
    }

    UIViewController *disappearingViewController = self.viewControllers.lastObject;
    [disappearingViewController sf_addTransitionNavigationBarIfNeeded];
    UIViewController *rootViewController = self.viewControllers.firstObject;
    if (rootViewController.sf_transitionNavigationBar) {
        UINavigationBar *appearingNavigationBar = rootViewController.sf_transitionNavigationBar;
        if (@available(iOS 15, *)) {
            self.navigationBar.standardAppearance = appearingNavigationBar.standardAppearance;
            self.navigationBar.scrollEdgeAppearance = appearingNavigationBar.scrollEdgeAppearance;
        } else {
            self.navigationBar.barTintColor = appearingNavigationBar.barTintColor;
            [self.navigationBar setBackgroundImage:[appearingNavigationBar backgroundImageForBarMetrics:UIBarMetricsDefault] forBarMetrics:UIBarMetricsDefault];
             self.navigationBar.shadowImage = appearingNavigationBar.shadowImage;
        }
    }
    if (animated) {
        disappearingViewController.navigationController.sf_backgroundViewHidden = YES;
    }
    return [self sf_popToRootViewControllerAnimated:animated];
}

- (void)sf_setViewControllers:(NSArray<UIViewController *> *)viewControllers animated:(BOOL)animated {
    UIViewController *disappearingViewController = self.viewControllers.lastObject;
    if (animated && disappearingViewController && ![disappearingViewController isEqual:viewControllers.lastObject]) {
        [disappearingViewController sf_addTransitionNavigationBarIfNeeded];
        if (disappearingViewController.sf_transitionNavigationBar) {
            disappearingViewController.navigationController.sf_backgroundViewHidden = YES;
        }
    }
    return [self sf_setViewControllers:viewControllers animated:animated];
}

#pragma mark - Getter & Setter
- (UIColor *)sf_containerViewBackgroundColor {
    return [UIColor whiteColor];
}

- (BOOL)sf_backgroundViewHidden {
    return [objc_getAssociatedObject(self, _cmd) boolValue];
}

- (void)setSf_backgroundViewHidden:(BOOL)hidden {
    objc_setAssociatedObject(self, @selector(sf_backgroundViewHidden), @(hidden), OBJC_ASSOCIATION_COPY_NONATOMIC);
    [[self.navigationBar valueForKey:@"_backgroundView"] setHidden:hidden];
}

- (UIViewController *)sf_transitionContextToViewController {
    return sf_objc_getAssociatedWeakObject(self, _cmd);
}

- (void)setSf_transitionContextToViewController:(UIViewController *)viewController {
    sf_objc_setAssociatedWeakObject(self, @selector(sf_transitionContextToViewController), viewController);
}

@end
